package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.utils.StringUtils;
import com.heima.common.aliyun.GreenImageUploadScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.feign.admin.AdminFeignClient;
import com.heima.feign.article.ArticleFeignClient;
import com.heima.file.service.MinioService;
import com.heima.model.media.dtos.WmNewsContentItem;
import com.heima.model.media.dtos.WmNewsDto;
import com.heima.model.media.dtos.WmNewsResultDTO;
import com.heima.model.media.enums.WmNewsStatusEnum;
import com.heima.wemedia.service.AuditService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmSensitiveService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 审核服务实现类
 *
 * @author Fuqiang
 * @since :2023-03-27
 */
@Slf4j
@Service
public class WmAuditServiceImpl implements AuditService {
    @Autowired
    WmNewsService newsService;

    @Autowired
    MinioService minioService;

    @Autowired
    WmSensitiveService sensitiveService;


    @Autowired
    GreenTextScan greenTextScan;

    @Autowired
    GreenImageUploadScan greenImageUploadScan;

    @Autowired
    private Tess4jClient tess4jClient;

    @Autowired
    ArticleFeignClient articleFeignClient;

    @Autowired
    AdminFeignClient adminFeignClient;


    /**
     * 异步审核自媒体文章, 自动审核. 并根据审核结果修改状态
     *
     * @param wmUserId  自媒体用户id
     * @param images    图片url的字符串格式
     * @param wmNewsDto 自媒体信息DTO
     * @param status 审核状态
     */
    @Async("taskExecutor")
    @Override
    public void auditWmNewsAsync(Integer wmUserId, String images, WmNewsDto wmNewsDto, WmNewsStatusEnum status) {
        log.info("开始异步审核");

        // 如果状态不为人工审核成功, 则开始审核, 若为人工审核成功, 则不开启审核
        if (!Objects.equals(WmNewsStatusEnum.ADMIN_SUCCESS, status)) {
            if (this.audit(wmNewsDto)) {
                return;
            }
        }

        // 审核通过
        Date publishTime = wmNewsDto.getPublishTime();
        if (publishTime == null) {
            publishTime = new Date();
            wmNewsDto.setPublishTime(publishTime);
        }
        // 如果
        if (publishTime.getTime() <= System.currentTimeMillis()) {
            //TODO 开始定时发布
        }
        WmNewsResultDTO wmNewsResultDTO = bulidWmNewsResultDTO(wmUserId, wmNewsDto, publishTime);
        //把wmNews发布成article Feign调用
        try {
            // 生成article,返回article_id
            Long articleId = articleFeignClient.createArticle(wmNewsResultDTO);
            //回填article_id， 修改审核状态
            newsService.updateWmNewsWithAuthResult(wmNewsDto.getId(), wmNewsDto.getReason(), WmNewsStatusEnum.PUBLISHED, articleId);

        } catch (Exception e) {
            log.error("feign远程调用失败:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }

    }

    private boolean audit(WmNewsDto wmNewsDto) {
        List<WmNewsContentItem> wmNewsContentItems =
                JSONObject.parseArray(wmNewsDto.getContent(), WmNewsContentItem.class);
        // 要审核的文本
        String auditText = getAuditText(wmNewsDto, wmNewsContentItems);
        // 要审核的敏感词
        List<String> sensitiveWords = sensitiveService.getSensitiveWord();
/*        // 要审核的图片集合
        List<byte[]> auditImagesByteList = getAuditImagesByteList(wmNewsDto, wmNewsContentItems);
        // 阿里云文本审核, 审核成功返回true, 审核失败返回false
        if (!this.aliGreenText(wmNewsDto, auditText)) {
            return true;
        }
        // 阿里云图片审核 审核成功返回true, 审核失败返回false
        if (!this.aliGreenImage(wmNewsDto, auditImagesByteList)) {
            return true;
        }*/
        // 敏感词审核
        if (!this.auditSensitiveWord(wmNewsDto, auditText, sensitiveWords)) {
            return true;
        }
        // 图片敏感词审核
/*
        if (!this.auditImagesSensitiveWord(wmNewsDto, auditImagesByteList, sensitiveWords)) {
            return;
        }
*/
        return false;
    }

    /**
     * 自定义的图片敏感词审核,审核成功返回true, 审核失败返回false
     *
     * @param wmNewsDto wmNewsDTO
     * @param auditImagesByteList 要审核的图片的byte数组集合
     * @param sensitiveWords      敏感词
     * @return 审核成功返回true, 审核失败返回false
     */
    private boolean auditImagesSensitiveWord(WmNewsDto wmNewsDto, List<byte[]> auditImagesByteList, List<String> sensitiveWords) {
        // 如果集合为空, 不需要审核, 直接返回
        if (CollectionUtils.isEmpty(auditImagesByteList)) {
            return true;
        }
        try {
            for (byte[] imageBytes : auditImagesByteList) {
                BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
                String imageText = tess4jClient.doOCR(bufferedImage);
                if (CollectionUtils.isEmpty(sensitiveWords)) {
                    return true;
                }
                Map<String, Integer> sensitiveAuditResultMap = SensitiveWordUtil.matchWords(sensitiveWords, imageText);
                if (!CollectionUtils.isEmpty(sensitiveAuditResultMap)) {
                    String reason = String.join(",", sensitiveAuditResultMap.keySet());
                    newsService.updateWmNewsWithAuthResult(wmNewsDto.getId(), reason, WmNewsStatusEnum.FAIL, null);
                    return false;
                }

            }
        } catch (Exception e) {
            log.error("图片敏感词审核异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
        return true;
    }

    /**
     * 阿里云文本审核
     *
     * @param wmNewsDto
     * @param auditText
     * @return 审核成功返回true; 审核失败返回false
     */
    private boolean aliGreenText(WmNewsDto wmNewsDto, String auditText) {

        try {
            Map<String, String> auditResultMap = greenTextScan.greenTextScan(auditText);
            String suggestion = auditResultMap.get("suggestion");
            // 如果结果正常  则继续执行
            if ("review".equals(suggestion)) {
                // 人工审核
                newsService.updateWmNewsWithAuthResult(wmNewsDto.getId(), auditResultMap.get("reason"), WmNewsStatusEnum.ADMIN_AUTH, null);
                return false;

            } else if ("block".equals(suggestion)) {
                // 拒绝 文本违规
                newsService.updateWmNewsWithAuthResult(wmNewsDto.getId(), auditResultMap.get("reason"), WmNewsStatusEnum.FAIL, null);
                return false;
            }
        } catch (Exception e) {
            log.error("阿里云文本审核异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
        return true;
    }


    /**
     * 阿里云图片审核
     *
     * @param wmNewsDto
     * @param auditImageList
     * @return 审核成功返回true; 审核失败返回false
     */
    private boolean aliGreenImage(WmNewsDto wmNewsDto, List<byte[]> auditImageList) {

        if (CollectionUtils.isEmpty(auditImageList)) {
            return true;
        }
        try {
            Map<String, String> auditImageResultMap = greenImageUploadScan.imageScan(auditImageList);
            String suggestion = auditImageResultMap.get("suggestion");
            // 如果结果正常  则继续执行
            if ("review".equals(suggestion)) {
                // 图片审核
                newsService.updateWmNewsWithAuthResult(wmNewsDto.getId(), auditImageResultMap.get("label"), WmNewsStatusEnum.ADMIN_AUTH, null);
                return false;
            } else if ("block".equals(suggestion)) {
                // 拒绝 图片违规
                newsService.updateWmNewsWithAuthResult(wmNewsDto.getId(), auditImageResultMap.get("label"), WmNewsStatusEnum.FAIL, null);
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("阿里云图片审核异常:{}", ExceptionUtils.getStackTrace(e));
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    /**
     * 获取审核的图片集合
     *
     * @param wmNewsDto
     * @param wmNewsContentItems
     * @return
     */
    private List<byte[]> getAuditImagesByteList(WmNewsDto wmNewsDto, List<WmNewsContentItem> wmNewsContentItems) {
        List<byte[]> auditImageList = new ArrayList<>();
        List<String> auditContentImageUrlList = wmNewsContentItems.stream().filter(item -> WmNewsContentItem.TYPE_IMAGE.equals(item.getType()))
                .map(WmNewsContentItem::getValue).collect(Collectors.toList());
        // 内容图片添加
        if (!CollectionUtils.isEmpty(auditContentImageUrlList)) {
            for (String url : auditContentImageUrlList) {
                auditImageList.add(minioService.downLoadFile(url));
            }
        }
        // 封面图片添加
        List<String> auditBodyImageUrlList = wmNewsDto.getImages();
        if (!CollectionUtils.isEmpty(auditBodyImageUrlList)) {
            for (String url : auditBodyImageUrlList) {
                auditImageList.add(minioService.downLoadFile(url));
            }
        }
        return auditImageList;
    }


    /**
     * 自定义的敏感词审核,审核成功返回true, 审核失败返回false
     *
     * @param wmNewsDto
     * @param auditText      要审核的文本
     * @param sensitiveWords 敏感词
     * @return
     */
    private boolean auditSensitiveWord(WmNewsDto wmNewsDto, String auditText, List<String> sensitiveWords) {
        Map<String, Integer> auditSensitiveWordResultMap =
                SensitiveWordUtil.matchWords(sensitiveWords, auditText);
        // 如果结果不为空 说明存在敏感词, 更新记录
        if (!CollectionUtils.isEmpty(auditSensitiveWordResultMap)) {
            String reason = String.join(",", auditSensitiveWordResultMap.keySet());
            newsService.updateWmNewsWithAuthResult(wmNewsDto.getId(), reason, WmNewsStatusEnum.FAIL, null);
            return false;
        }
        return true;
    }

    /**
     * 获取审核的文本
     *
     * @param wmNewsDto
     * @param wmNewsContentItems
     * @return
     */
    private String getAuditText(WmNewsDto wmNewsDto, List<WmNewsContentItem> wmNewsContentItems) {
        String auditTitle = wmNewsDto.getTitle();
        String auditLabels = wmNewsDto.getLabels();
        String auditContent = this.getWmNewsContentByText(wmNewsContentItems);

        return auditTitle + "-" + auditContent + "-" + auditLabels;
    }

    /**
     * 构建WmNewsResultDTO对象
     *
     * @param wmUserId
     * @param wmNewsDto
     * @param publishTime
     * @return
     */
    private WmNewsResultDTO bulidWmNewsResultDTO(Integer wmUserId, WmNewsDto wmNewsDto, Date publishTime) {

        WmNewsResultDTO wmNewsResultDTO = new WmNewsResultDTO();

        Integer channelId = wmNewsDto.getChannelId();
        // 调用feign获取channelName
        String channelName = adminFeignClient.getChannelNameById(channelId);

        wmNewsResultDTO.setId(wmNewsDto.getId());
        wmNewsResultDTO.setWmUserId(wmUserId);
        wmNewsResultDTO.setTitle(wmNewsDto.getTitle());
        wmNewsResultDTO.setContent(wmNewsDto.getContent());
        wmNewsResultDTO.setType(wmNewsDto.getType());

        wmNewsResultDTO.setChannelId(channelId);
        wmNewsResultDTO.setChannelName(StringUtils.isEmpty(channelName) ? "" : channelName);
        wmNewsResultDTO.setLabels(wmNewsDto.getLabels());
        wmNewsResultDTO.setCreatedTime(new Date());
        wmNewsResultDTO.setSubmitedTime(new Date());
        wmNewsResultDTO.setPublishTime(publishTime);
        wmNewsResultDTO.setImages(CollectionUtils.isEmpty(
                wmNewsDto.getImages()) ? null : String.join(",", wmNewsDto.getImages()));
        wmNewsResultDTO.setStatus(wmNewsDto.getStatus());
        wmNewsResultDTO.setReason("");
        wmNewsResultDTO.setEnable(wmNewsDto.getEnable());
        return wmNewsResultDTO;

    }


    /**
     * 获取 content中的文字部分
     *
     * @param wmNewsContentItems
     * @return
     */
    private String getWmNewsContentByText(List<WmNewsContentItem> wmNewsContentItems) {
        return wmNewsContentItems.stream().filter(item -> WmNewsContentItem.TYPE_TEXT.equals(item.getType()))
                .map(WmNewsContentItem::getValue).collect(Collectors.joining("-"));
    }
}
